﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using LowLevelGraphics.Filter;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Skeletonization like described in dspguide pdf
    /// </summary>
    public class ExperimentalSkeletonize : ExperimentalMorphology
    {
        protected int m_nIterations = 5;
        protected bool m_bAutoStop = true;

        /// <summary>
        /// Initializes a new instance of the <see cref="ExperimentalSkeletonize"/> class.
        /// </summary>
        public ExperimentalSkeletonize()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ExperimentalSkeletonize"/> class.
        /// </summary>
        /// <param name="_nIterations">The _n iterations.</param>
        public ExperimentalSkeletonize(int _nIterations)
        {
            m_nIterations = _nIterations;
        }

        /// <summary>
        /// Experimental1s the skeletonize.
        /// </summary>
        /// <param name="_experimentalSkeletonize">The _experimental skeletonize.</param>
        internal ExperimentalSkeletonize(ExperimentalSkeletonize _experimentalSkeletonize)
        {
        }

        /// <summary>
        /// Gets or sets the iterations.
        /// </summary>
        /// <value>The iterations.</value>
        public int Iterations
        {
            get { return m_nIterations; }
            set { m_nIterations = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [auto stop].
        /// If autostop is set to true, the iterations will be cancelled 
        /// if no more pixels have changed in the current iteration
        /// </summary>
        /// <value><c>true</c> if [auto stop]; otherwise, <c>false</c>.</value>
        public bool AutoStop
        {
            get { return m_bAutoStop; }
            set { m_bAutoStop = value; }
        }

        /// <summary>
        /// Executes the specified _bitmap.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            int nWidth = _bitmap.Width;
            int nHeight = _bitmap.Height;

            Color color = Color.Transparent;
            int i = 0;
            int y = 0;
            int x = 0;

            int nStoppedAtIteration = -1;

            for (i = 0; i < m_nIterations; i++)
            {
                int nIterationalCount = 0;
                for (y = 1; y < nHeight - 1; y++)
                {
                    for (x = 1; x < nWidth - 1; x++)
                    {
                        color = _bitmap.GetPixel(x, y);
                        MarkPixels(_bitmap, x, y, color);
                    }
                }

                for (y = 0; y < nHeight; y++)
                {
                    for (x = 0; x < nWidth; x++)
                    {
                        color = _bitmap.GetPixel(x, y);
                        if (color == Color.FromArgb(1))
                        {
                            _bitmap.SetPixel(x, y, Definitions.White);
                            nIterationalCount++;
                        }
                    }
                }
                if (m_bAutoStop && nIterationalCount == 0)
                {
                    nStoppedAtIteration = i;
                    break;
                }
            }
            return _bitmap;
        }

        /// <summary>
        /// Marks the pixels.
        /// </summary>
        /// <param name="nX">The n X.</param>
        /// <param name="nY">The n Y.</param>
        /// <param name="_color">The _color.</param>
        public void MarkPixels(UnsafeBitmap _bitmap, int nX, int nY, Color _color)
        {
            //Rule 1: Do nothing if pixel is already wite
            if (_bitmap.GetPixel(nX, nY) == Definitions.White) return;

            //Rule 2: Do nothing if all of the close neighbours are black
            if (_bitmap.GetPixel(nX - 1, nY) != Definitions.White
                && _bitmap.GetPixel(nX + 1, nY) != Definitions.White
                && _bitmap.GetPixel(nX, nY - 1) != Definitions.White
                && _bitmap.GetPixel(nX, nY + 1) != Definitions.White)
            {
                return;
            }

            //Rule 3: Do nothing if only a single neighbour is black
            int nCount = 0;
            foreach (Point pt in Definitions.EnvironmentPointListClockwise)
            {
                if (_bitmap.GetPixel(nX + pt.X, nY + pt.Y) == Definitions.Black) nCount++;
            }
            if (nCount == 1)
            {
                return;
            }

            //Rule 4: Do nothing if the neighbors are unconnected
            //Determine this by counting the black-to-white transitions
            // while moving clockwise through the 8 neighboring pixels.
            if (_bitmap[nX - 1, nY - 1] == Definitions.Black && _bitmap[nX, nY - 1] != Definitions.Black) nCount = nCount + 1;
            if (_bitmap[nX, nY - 1] == Definitions.Black && _bitmap[nX + 1, nY - 1] != Definitions.Black && _bitmap[nX + 1, nY] != Definitions.Black) nCount = nCount + 1;
            if (_bitmap[nX + 1, nY - 1] == Definitions.Black && _bitmap[nX + 1, nY] != Definitions.Black) nCount = nCount + 1;
            if (_bitmap[nX + 1, nY] == Definitions.Black && _bitmap[nX + 1, nY + 1] != Definitions.Black && _bitmap[nX, nY + 1] != Definitions.Black) nCount = nCount + 1;
            if (_bitmap[nX + 1, nY + 1] == Definitions.Black && _bitmap[nX, nY + 1] != Definitions.Black) nCount = nCount + 1;
            if (_bitmap[nX, nY + 1] == Definitions.Black && _bitmap[nX - 1, nY + 1] != Definitions.Black && _bitmap[nX - 1, nY] != Definitions.Black) nCount = nCount + 1;
            if (_bitmap[nX - 1, nY + 1] == Definitions.Black && _bitmap[nX - 1, nY] != Definitions.Black) nCount = nCount + 1;
            if (_bitmap[nX - 1, nY] == Definitions.Black && _bitmap[nX - 1, nY - 1] != Definitions.Black && _bitmap[nX, nY - 1] != Definitions.Black) nCount = nCount + 1;
            if (nCount > 1) return;

            //Mark to set to white
            _bitmap[nX, nY] = Color.FromArgb(1);
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new ExperimentalSkeletonize(this);
        }
    }
}
